﻿using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using RabbitMq.ServerApi.MessageManager;
using RabbitMq.ServerApi.Model;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

namespace RabbitMq.ServerApi.Controllers
{

    /// <summary>
    /// 
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    public class SchoolNoticesController : ControllerBase
    {
        private readonly ILogger<SchoolNoticesController> _logger;
        private readonly RabbitMQMessageService _rabbitMqService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="logger"></param>
        public SchoolNoticesController(ILogger<SchoolNoticesController> logger)
        {
            _logger = logger;
            _rabbitMqService = new RabbitMQMessageService();
        }


        /// <summary>
        /// 扇形交换机-生产者（Fanout Exchange）
        /// </summary>
        /// <param name="schoolNotice"></param>
        /// <returns></returns>
        [HttpPost("addNotices")]
        public SchoolNoticeDto CreateNotices(SchoolNoticeDto schoolNotice)
        {
            #region 1、生产者
            {
                //// 1、创建连接工厂
                //var factory = new ConnectionFactory()
                //{
                //    HostName = "localhost",
                //    Port = 5672,
                //    Password = "guest",
                //    UserName = "guest",
                //    VirtualHost = "/"
                //};
                using (var connection = _rabbitMqService.GetConnection())
                {
                    var channel = connection.CreateModel();
                    // 2、定义队列
                    channel.QueueDeclare(queue: "test_fanout_message",
                                         durable: true,// 消息持久化(防止rabbitmq宕机导致队列丢失风险)
                                         exclusive: false,
                                         autoDelete: false,
                                         arguments: null);

                    string productJson = JsonConvert.SerializeObject(schoolNotice);
                    // string message = "Hello World!";
                    var body = Encoding.UTF8.GetBytes(productJson);

                    // 3、发送消息
                    var properties = channel.CreateBasicProperties();
                    properties.Persistent = true; // 设置消息持久化（个性化控制）
                    channel.BasicPublish(exchange: "",
                                         routingKey: "test_fanout_message", //如果没有设置routingKey则发送所有队列
                                         basicProperties: properties,
                                         body: body);
                }
                Console.WriteLine("成功发布通知......");
                _logger.LogInformation("成功发布通知");
            }

            return schoolNotice;
            #endregion
        }

        /// <summary>
        /// 添加通知-直连交换机-生产者（Direct Exchange）
        /// </summary>
        /// <returns></returns>
        [HttpPost("teacherNotice")]
        public SchoolNoticeDto CreateTeacherNotices(SchoolNoticeDto dto)
        {
            /* 
                定义：直连交换机会将消息路由到那些路由键（routing key）与队列绑定时指定的路由键完全匹配的队列中。
                区别：它是基于精确匹配的路由策略，即消息携带的路由键必须与队列绑定的路由键完全一致才能被投递到对应队列。
                使用场景：适用于简单的点对点通信场景，当消息具有明确的目的地，且每个消息都需要发送到特定的队列时。
             */
            //1、创建连接
            using (var connection = _rabbitMqService.GetConnection())
            {
                var channel = connection.CreateModel();
                //2、定义交换机
                channel.ExchangeDeclare(exchange: "adduser_direct", type: ExchangeType.Direct);

                string noticeJson = JsonConvert.SerializeObject(dto);
                var body = Encoding.UTF8.GetBytes(noticeJson);

                //3、发送消息
                var properties = channel.CreateBasicProperties();
                properties.Persistent = true;//设置消息持久化
                channel.BasicPublish(exchange: "adduser_direct",
                    routingKey: "notices_key",
                    basicProperties: properties,
                    body: body);
            }
            Console.WriteLine("成功发布通知....");
            return dto;

        }

        /// <summary>
        /// 发出通知（Rpc模式）
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("rpcPublish")]
        public SchoolNoticeDto CreateRpcDirect(SchoolNoticeDto dto)
        {
            /*
             * RPC模式特点：
             * 1、客户端既是生产者也是消费者。它首先创建一个临时队列（unique reply queue），并将其绑定到一个交换机（通常是Direct交换机）
             * 2、客户端发送一个RPC请求作为消息到RabbitMQ，消息中包含要执行的方法名，参数以及一个回复临时队列
             * 3、服务端监听某个特定的队列（这个队列绑定到了接收请求的交换机上），当接收到消息后执行相应的方法，并将结果作为新的消息发送出去，目标是客户端提供的回复队列
             * 4、客户端等待并消费从其临时回复队列中收到的消息，一旦接收消息，就认为是RPC调用的结果，然后进行后续的处理并清理临时资源。
             * **/

            //1、创建连接
            //var factory = new ConnectionFactory()
            //{
            //    HostName = "localhost",
            //    Port = 5672,
            //    Password = "guest",
            //    UserName = "guest",
            //    VirtualHost = "/"
            //};

            using (var connection = _rabbitMqService.GetConnection())
            {
                var channel = connection.CreateModel();
                //2、定义交换机
                string queueName = channel.QueueDeclare().QueueName;

                var properties = channel.CreateBasicProperties();
                var correlationId = Guid.NewGuid().ToString();
                properties.CorrelationId = correlationId;
                properties.ReplyTo = queueName;

                //3、发送消息
                var messageJson = JsonConvert.SerializeObject(dto);
                var body = Encoding.UTF8.GetBytes(messageJson);
                properties.Persistent = true;//消息持久化
                channel.BasicPublish(exchange: "",
                    routingKey: "teacher_add",
                    basicProperties: properties,
                    body: body);

                //4、消息回调
                var consumer = new EventingBasicConsumer(channel);
                consumer.Received += (model, ea) =>
                {
                    Console.WriteLine($"model:{model}");
                    var body = ea.Body;
                    var message = Encoding.UTF8.GetString(body.ToArray());
                    Console.WriteLine($"message:{message}");
                    if (ea.BasicProperties.CorrelationId == correlationId)
                    {
                        Console.WriteLine(" [x] 回调成功！{0}", message);
                    }
                };

                //5、消费消息(每次1个消费者只成功消费1个,防止多个 消费者争抢导致系统问题（微服务系统中）)
                channel.BasicQos(0, 1, false);
                channel.BasicConsume(queue: queueName,
                    autoAck: true,//消息确认,防止消费失败
                    consumer: consumer);
            }
            Console.WriteLine("成功发布作业通知...");
            return dto;

        }

        /// <summary>
        /// 主题交换机（Topic Exchange）
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("topicPublish")]
        public TeacherUserDto UpdateUserAsync(TeacherUserDto dto)
        {
            /*
             * 主题交换机的核心是可以以范围向队列发送消息，它和直连交换机的区别在于：直连交换机通过一个binding_key和交换机的
             * 定义：主题交换机根据消息的路由键与队列的绑定键之间的模式匹配来路由消息。绑定键可以包含通配符*（匹配任意单词）和#（匹配零个或多个单词）。
             * 区别：支持灵活的主题订阅机制，允许队列根据某种主题规则过滤消息。
             * 使用场景：适用于多级或多维度的订阅系统，可以根据消息内容的不同部分动态路由消息到不同的队列。
             * **/

            //以下场景：修改用户数据后,发送短信通知
            using (var connection = _rabbitMqService.GetConnection())
            {

                var channel = connection.CreateModel();
                // 2、定义交换机
                channel.ExchangeDeclare(exchange: "sms_topic", type: ExchangeType.Topic);

                string productJson = JsonConvert.SerializeObject(dto);
                // string message = "Hello World!";
                var body = Encoding.UTF8.GetBytes(productJson);

                // 3、发送消息
                var properties = channel.CreateBasicProperties();
                properties.Persistent = true; // 设置消息持久化
                channel.BasicPublish(exchange: "sms_topic",
                                     routingKey: "sms.user.update",
                                     basicProperties: properties,
                                     body: body);

            }
            _logger.LogInformation("成功发送通知...");
            return dto;

        }
        /// <summary>
        /// 主题交换机（Topic Exchange）
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("topicPublish2")]
        public TeacherUserDto CreateUserAsync(TeacherUserDto dto)
        {
            /*
             * 主题交换机的核心是可以以范围向队列发送消息，它和直连交换机的区别在于：直连交换机通过一个binding_key和交换机的
             * 定义：主题交换机根据消息的路由键与队列的绑定键之间的模式匹配来路由消息。绑定键可以包含通配符*（匹配任意单词）和#（匹配零个或多个单词）。
             * 区别：支持灵活的主题订阅机制，允许队列根据某种主题规则过滤消息。
             * 使用场景：适用于多级或多维度的订阅系统，可以根据消息内容的不同部分动态路由消息到不同的队列。
             * **/

            //以下场景：修改用户数据后,发送短信通知
            using (var connection = _rabbitMqService.GetConnection())
            {

                var channel = connection.CreateModel();
                // 2、定义交换机
                channel.ExchangeDeclare(exchange: "sms_topic", type: ExchangeType.Topic);

                string productJson = JsonConvert.SerializeObject(dto);
                // string message = "Hello World!";
                var body = Encoding.UTF8.GetBytes(productJson);

                // 3、发送消息
                var properties = channel.CreateBasicProperties();
                properties.Persistent = true; // 设置消息持久化
                channel.BasicPublish(exchange: "sms_topic",
                                     routingKey: "sms.user.create",
                                     basicProperties: properties,
                                     body: body);

            }
            _logger.LogInformation("成功发送通知...");
            return dto;

        }



    }
}
